<?php

namespace app\index\controller;

use think\Request;
use app\index\BaseController;
use app\common\model\Question;
use app\common\model\Type;
use think\Image;
use think\image\Exception as ImageException;
use app\common\services\PrivateMessage as PrivateMessageService;
use app\common\model\payload\Edit as EditPayload;
use app\common\model\payload\Check as CheckPayload;
use app\common\model\QuestionExam;
use app\common\model\User;
use app\common\services\TypeConsistent;
use app\common\util\JsDataTime;
use app\http\middleware\CheckAuth;
use think\Db;

class Questions extends BaseController
{
    const STAUTS_NOT_CHECK = 0;
    const STAUTS_PASS = 1;
    const STAUTS_FAIL = 2;

    /**
     * 根据参数查询题目，分页
     *
     * @return \think\Response
     * @args status+ 题目状态，null则查询全部
     * @args style+ 题目形式，0选择，1填空
     * @args content+ 题目内容，将在题干、选项、答案中搜索
     * @args is_me+ 是否只查询自己题目
     * @args page? 页码
     */
    public function queryQuestions(Request $request)
    {
        $query = Question::with(['author']);

        if ($request->has('status')) {
            $query = $query->where('status', $request->status);
        }

        if ($request->has('style')) {
            $query = $query->where('style', intval($request->style));
        }

        if ($request->has('content')) {
            $query = $query->where('text|options|answer', 'like', '%' . $request->content . '%');
        }

        if ($request->has('is_me') && boolval($request->is_me)) {
            $user = getCurrentUser();
            $query = $query->where('author_id', $user->id);
        }

        return json($query->paginate(15));
    }

    /**
     * 根据ID获取题目，不存在时返回null
     *
     * @return \think\Response
     * @args id 题目ID
     */
    public function getQuestion($id)
    {
        return json(
            Question::with(['author'])->where('id', $id)->find()
        );
    }

    /**
     * 创建题目
     *
     * @return \think\Response
     * @args type_id 题目类型ID
     * @args style 题目形式，0选择，1填空
     * @args text+ 题干
     * @args options+ 选项
     * @args answer+ 答案
     */
    public function createQuestion()
    {
        $this->validateOrError();

        $type_id = input('type_id');
        $style = input('style');
        $text = input('text');
        $options = input('options');
        $answer = input('answer');
        $style = intval($style);

        $author = getCurrentUser();

        if (!(Type::where('id', $type_id)->find())) {
            error('题目类型不存在');
        }

        if ($style === 1) {
            $options = [];
        }

        $question = new Question(\compact([
            'type_id',
            'style',
            'text',
            'options',
            'answer',
        ]));
        $question->author_id = $author->id;
        $question->status = self::STAUTS_NOT_CHECK;
        $question->pass_count = 0;
        $question->fail_count = 0;
        $question->is_exported = false;
        $question->save();

        return json($question);
    }

    /**
     * 上传图片
     *
     * @return \think\Response
     * @formdata
     * @args id 题目ID
     * @args image+ 图片，图片尺寸最大512x340
     * @args auto_clip? 是否自动剪裁，默认否
     */
    public function uploadImage(Request $request, $id, CheckAuth $checkAuth)
    {
        $auto_clip = input('auto_clip', false);
        $imageFile = $request->file('image');

        if (!$imageFile) {
            error('未找到图片，上传的文件太大了？');
        }

        // 检查文件大小，不能超过1MB
        $size = $imageFile->getInfo('size');
        if ($size > 1 * 1024 * 1024) {
            error('图片大小不能超过1M哦');
        }

        /** @var Question $question */
        $question = Question::where('id', $id)->find();
        if (!$question) {
            error('题目不存在，无法上传图片');
        }

        $currentUser = getCurrentUser();
        if (!$checkAuth->can('questions.direct_edit') && !$question->isAuthor($currentUser)) {
            error('您不是题目作者，不能编辑此题目');
        }

        // 删除已存在的图片
        $question->removeImageIfExist();

        // 打开图片，获取类型、宽高
        try {
            $image = Image::open($imageFile);
        } catch (ImageException $e) {
            error('图片异常' . $e->getMessage());
        }
        $type = $image->type();
        $width = $image->width();
        $height = $image->height();

        // 检查类型
        if (!in_array($type, ['jpg', 'png', 'jpeg'])) {
            error('图片类型无效，必须是jpg或png图片');
        }

        // 检查宽高
        if (!$auto_clip && ($width > 512 || $height > 340)) {
            error('图片尺寸太大，最大尺寸为宽x高=512x340(px)');
        }

        // 根据ID创建image属性
        $question->image = $question->buildImageName($type);

        // 保存图片
        if ($auto_clip) {
            $image->thumb(512, 340)->save($question->image_path);
        } else {
            $image->save($question->image_path);
        }

        $question->status = self::STAUTS_NOT_CHECK;
        $question->save();

        return json($question);
    }

    /**
     * 修改题目信息
     *
     * @return \think\Response
     * @args id 题目ID
     * @args type_id+ 题目类型ID
     * @args style+ 题目形式，0选择，1填空
     * @args text+ 题干
     * @args options+ 选项
     * @args answer+ 答案
     * @args commit+ 审题员编辑记录
     */
    public function editQuestion(Request $request, $id, PrivateMessageService $service, CheckAuth $checkAuth)
    {
        $this->validateOrError();

        $type_id = input('type_id');
        $style = input('style');
        $text = input('text');
        $options = input('options');
        $answer = input('answer');
        $style = intval($style);
        $commit = input('commit');

        /** @var Question $question */
        $question = Question::where('id', $id)->find();
        if (!$question) {
            error('题目不存在，无法编辑');
        }

        if ($style === 1) {
            $options = [];
        }

        $currentUser = getCurrentUser();
        $subUpdate = false;
        $subMessage = false;
        if ($question->isAuthor($currentUser)) {
            $subUpdate = true;
            $subMessage = false;
        } else if ($checkAuth->can('questions.direct_edit')) {
            if (!$request->has('commit') || empty($commit)) {
                error('编辑记录不能为空，要认真填写你修改了什么哦！');
            }
            $subUpdate = true;
            $subMessage = true;
        } else {
            error('您不是题目作者，也不是审核人员，不能编辑此题目');
        }

        if ($subUpdate) {
            // 更新信息
            $question->type_id = $type_id;
            $question->style = $style;
            $question->text = $text;
            $question->options = $options;
            $question->answer = $answer;

            // 状态转为未审核
            $question->status = self::STAUTS_NOT_CHECK;

            // 保存
            $question->save();
        }

        if ($subMessage) {
            // 发送私信通知结果
            $paylod = new EditPayload($question->id);
            $checkerName = $currentUser->realname;
            $content = "{$checkerName}编辑了你的题目：" . $commit . "，点击下面的链接查看该题目。";
            $service->with($paylod)->send($content)->to($question->author);
        }

        return json($question);
    }

    /**
     * 删除指定资源
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function removeQuestion($id)
    {
        /** @var Question $question */
        $question = Question::where('id', $id)->find();
        if (!$question) {
            error('题目不存在');
        }

        $currentUser = getCurrentUser();
        if (!$question->isAuthor($currentUser)) {
            error('您不是题目作者，不能删除此题目');
        }

        // TODO 删除题目相关的消息
        $question->removeImageIfExist();
        QuestionExam::where('question_id', $id)->delete();

        $question->delete();
        return json([
            'message' => '删除成功'
        ]);
    }

    /**
     * 随机返回一个未审核的题目
     *
     * @return \think\Response
     */
    public function getRandomUncheckedQuestion()
    {
        $question = Question::with(['author'])
            ->where('status', self::STAUTS_NOT_CHECK)
            ->orderRaw('RAND()')
            ->find();
        return json(
            $question
        );
    }

    /**
     * 审核题目
     *
     * @param  int  $id
     * @return \think\Response
     * @args id 题目ID
     * @args status 题目审核结果
     * @args reason 拒绝理由
     */
    public function checkQuestion(Request $request, $id, PrivateMessageService $service)
    {
        $this->validateOrError();

        $status = input('status');
        $reason = input('reason');
        $status = intval($status);

        $currentUser = getCurrentUser();

        /** @var Question $question */
        $question = Question::where('id', $id)->find();
        if (!$question) {
            error('题目不存在，无法审核');
        }

        if ($question->author_id === $currentUser->id) {
            error('你不能审核自己的题哦！');
        }

        $subUpdate = false;
        $subMessage = false;
        if ($status === 1) {
            $subUpdate = true;
            $subMessage = false;
        } else if ($status === 2) {
            if (!$request->has('reason') || empty($reason)) {
                error('拒绝理由不能为空，要好好填写拒绝理由哦');
            }
            $subUpdate = true;
            $subMessage = true;
        } else {
            error('服务器内部错误：不可达代码status=' . $status); // never reached
        }

        if ($subUpdate) {
            $question->status = $status;
            $question->save();
        }

        // 审核结果不通过时删除做题记录
        if ($status === 2) {
            QuestionExam::where('question_id', $id)->delete();
        }

        if ($subMessage) {
            // 发送私信通知结果
            $paylod = new CheckPayload($question->id);
            $checkerName = $currentUser->realname;
            $content = "{$checkerName}拒绝了你的题目，理由是：" . $reason . "，点击下面的链接查看该题目。";
            $service->with($paylod)->send($content)->to($question->author);
        }

        return json([
            'message' => '审核成功',
        ]);
    }

    /**
     * 查询题目，附带当前用户做题结果
     *
     * @param  int  $id
     * @return \think\Response
     * @args page 页码
     */
    public function queryQuestionsWithExam()
    {
        $user = getCurrentUser();

        $data = $this->getQueryWithExam($user->id)
            ->where('q.status', self::STAUTS_PASS) // 只查询已经通过的题目
            ->paginate(15);

        return json(
            $data->each(function ($item, $key) {
                return $this->formatQuestionExam($item);
            })
        );
    }

    /**
     * 获取下一个当前用户未做过的题目
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function getNextUnexamedQuestion()
    {
        $user = getCurrentUser();

        $question = $this->getQueryWithExam($user->id)
            ->where('q.status', self::STAUTS_PASS) // 只查询已经通过的题目
            ->whereNull('e.examinee_id')
            ->find();

        return json(
            $this->formatQuestionExam($question)
        );
    }

    /**
     * @param int $currentUserId
     */
    private function getQueryWithExam($currentUserId)
    {
        // 子查询筛选出所有当前用户的做题记录
        $subsql = Db::table('question_exams')
            ->alias('e')
            ->where('examinee_id', $currentUserId)
            ->buildSql();

        // 主查询
        $query = Question::alias('q')
            ->with(['author'])
            ->leftJoin([$subsql => 'e'], 'q.id = e.question_id') // LEFT JOIN 联表查询可以查出未做的题
            ->field([ // 解决字段冲突
                'q.*',
                'e.examinee_id' => 'exam_examinee_id',
                'e.result' => 'exam_result',
                'e.create_at' => 'exam_create_at',
                'e.update_at' => 'exam_update_at',
            ]);

        return $query;
    }

    private function formatQuestionExam(&$item)
    {
        if ($item === null) {
            return $item;
        }
        $fields = ['exam_create_at', 'exam_update_at'];
        foreach ($fields as $key) {
            $d = new JsDataTime($item->{$key});
            $item->{$key} = $d->jsonSerialize();
        }
        if ($item->exam_result === null) {
            $item->exam_result = 0;
        }
        return $item;
    }

    /**
     * 做题
     *
     * @param  int  $id
     * @return \think\Response
     * @args id 题目ID
     * @args result 受试结果
     */
    public function examQuestion($id)
    {
        $this->validateOrError();

        $result = input('result');
        $result = intval($result);

        /** @var Question $question */
        $question = Question::where('id', $id)->find();
        if (!$question) {
            error('题目不存在，无法刷题');
        }

        $currentUser = getCurrentUser();

        /** @var QuestionExam $exam */
        $exam = QuestionExam::where('question_id', $id)
            ->where('examinee_id', $currentUser->id)
            ->find();

        // 找不到则新建
        if (!$exam) {
            $exam = new QuestionExam();
            $exam->question_id = $id;
            $exam->examinee_id = $currentUser->id;
        }

        // 保存做题结果
        $exam->result = $result;
        $exam->save();

        // 更新题目中的缓存数字
        $question->updateExamCount();

        return json([
            'message' => '审核成功',
        ]);
    }

    /**
     * 获取题目统计数据
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function getQuestionStatistics()
    {
        $types = Type::where(1)->select();
        $consistent = new TypeConsistent($types);
        $result = $consistent->checkConsistent(255);

        if (!$result['result']) {
            error('题目类型树无效：' . $result['message'] . '，无法进行统计', 501);
        }

        if (!$result['valid']) {
            error('题目类型树一致性有误，必须修复后才能获取统计数据', 501);
        }

        /** @var Type[] $rootTypes */
        $rootTypes = $consistent->getRootTypeList();

        $statistics = [
            'type' => [],
            'style' => [],
            'status' => [],
            'mine_status' => [],
        ];

        // 按题目类型统计
        foreach ($rootTypes as $type) {
            $n = Question::alias('q')
                ->join(['types' => 't'], 'q.type_id = t.id')
                ->where('t.root_type_id', $type->id)
                ->count();

            $statistics['type'][] = [
                'id' => $type->id,
                'name' => $type->type_name,
                'count' => $n,
            ];
        }

        // 按题目形式统计
        $statistics['style'][] = [
            'style' => 0,
            'name' => '选择题',
            'count' => Question::where('style', 0)->count(),
        ];
        $statistics['style'][] = [
            'style' => 1,
            'name' => '填空题',
            'count' => Question::where('style', 1)->count(),
        ];

        // 按题目审核结果统计
        $statistics['status'][] = [
            'status' => 0,
            'name' => '未审核题目',
            'count' => Question::where('status', 0)->count(),
        ];
        $statistics['status'][] = [
            'status' => 1,
            'name' => '通过的题目',
            'count' => Question::where('status', 1)->count(),
        ];
        $statistics['status'][] = [
            'status' => 2,
            'name' => '未通过题目',
            'count' => Question::where('status', 2)->count(),
        ];

        // 我出的题
        $currentUser = getCurrentUser();
        $statistics['mine_status'][] = [
            'status' => 0,
            'name' => '未审核题目',
            'count' => Question::where('status', 0)->where('author_id', $currentUser->id)->count(),
        ];
        $statistics['mine_status'][] = [
            'status' => 1,
            'name' => '通过的题目',
            'count' => Question::where('status', 1)->where('author_id', $currentUser->id)->count(),
        ];
        $statistics['mine_status'][] = [
            'status' => 2,
            'name' => '未通过题目',
            'count' => Question::where('status', 2)->where('author_id', $currentUser->id)->count(),
        ];

        return json($statistics);
    }

    /**
     * 按作者统计题库数据
     *
     * @param  int  $id
     * @return \think\Response
     * @args begin 开始时间
     * @args end 结束时间
     */
    public function getQuestionStatisticsByAuthor()
    {
        $this->validateOrError();

        $begin = input('begin') . ' 00:00:00';
        $end = input('end') . ' 23:59:59';

        $users = User::order('role')->select();
        $result = [];
        foreach ($users as $user) {
            /** @var User $user */
            $curr = [
                'id' => $id = $user->id,
                'realname' => $user->realname,
                'total' => Question::where('author_id', $id)->where('create_at', '>', $begin)->where('create_at', '<', $end)->count(),
                'pass' => Question::where('author_id', $id)->where('create_at', '>', $begin)->where('create_at', '<', $end)->where('status', 1)->count(),
                'fail' => Question::where('author_id', $id)->where('create_at', '>', $begin)->where('create_at', '<', $end)->where('status', 2)->count(),
            ];
            $result[] = $curr;
        }
        return json($result);
    }
}
